home *** CD-ROM | disk | FTP | other *** search
/ MACD 5 / MACD 5.bin / workbench / tools / czesc_1 / dr / source / dr.c next >
C/C++ Source or Header  |  1993-11-21  |  59KB  |  2,384 lines

  1. /* TO DO:
  2. option letters available: E G J Q W  .... maybe:
  3.    q = supercompaQt columns
  4.    g = aGe
  5.    j = Jump into soft links when -R / ::?
  6.  
  7. Filter cases where a multi-assign was already covered by recursion?
  8. Hard links in -r listing can end up with linkee's name??
  9. Add \:::f and \:::p?  \" for quote if space in pathname?
  10. Someday make -P default to -P~H instead of no mask, if C= ever does likewise.
  11. Add date-string parsing to -A and -B with syntax -A(date time).  -Adays.hours?
  12. Make it show both -X info and -R info when both are specified.
  13. Make -P able to have or-ed sets of bits, like -p~as|~ap ?
  14. -G shows file's age in days hh:mm:ss?
  15. Option -Q for super-compact columnation?  Always use super-compact?
  16. Avoid repeating "unknown option" for same letter?
  17.  
  18. Note: when -R goes into a soft link to a file (presently disabled), it shows
  19.    the file as if it were inside the soft link, rather than being what it's
  20.    linked to.
  21. */
  22.  
  23. /* ==========================================================================
  24.  
  25. The idea here is to make Yet Another Cli Directory Command.  What's special
  26. about this one?  It's really really fast, it's got more goddamn features than
  27. you can believe, and by default it doesn't show .info files.  Instead it just
  28. shows all files that have .info's associated with them by writing the name in
  29. orange instead of white when output is to the screen.  And it puts file names
  30. (and directory names also) in as many columns (up to ten) as will fit
  31. comfortably in the window.  And everything is alphabetized in columns.  It will
  32. also do Amiga patterns.  It tries a pattern first as an exact literal before
  33. expanding it, in case you have a drawer named "Doesn't work?" or something.  And
  34. it does recursive descents, faster than the Fast File System.  It is intended to
  35. replace Dir, List, rls, and du, and to outperform all competitors.
  36.  
  37. Dr is written for Aztec C 5.2b for Amiga, by Paul Kienitz.  Public Domain.  See
  38. the files Dr.doc and FastExNext.doc for useful information.  Documentation for
  39. smallio.c is in the source, kind of.  Look in pureio.c (included with older
  40. versions of Dr) if you really want to know more.
  41. ========================================================================== */
  42.  
  43. /* some #defines which you can make different verisons with:
  44.     if C_NOT_ASM it does not use inline assembly language instead of C
  45.         for some sorting functions (which won't work with Lattice/SAS).
  46.     if DEBUG is defined it may produce extra error messages
  47.     if MARKLINKS is defined then a -L style output will mark hard and soft
  48.         links with H> and S>.
  49. */
  50.  
  51. #define VERSION     "2.0"
  52.  
  53. #define FLACK       ':'        /* NOT USED */
  54.  
  55. #define WIDEFAULT   77
  56.  
  57. #define HYPH        0x96
  58.  
  59. #define HELP        0x9F
  60.  
  61. #define CSI         "\x9B"
  62.  
  63. #define MAXCOLS     10
  64.  
  65. #define PREPENGTH   300
  66.  
  67. #define LOOPDEPTH   30
  68.  
  69. #define STACKNEEDED (1500 + 400)
  70.  
  71. #define PATLIMIT    128
  72.  
  73. #define ABSIZE      300L
  74.  
  75. #define SOFTSIZE    288L
  76.  
  77. #define MULTILIMIT  50
  78.  
  79. #define TOPSTYLE    3
  80. /* styles: 0 = dented, 1 = blankline, 2 = stupid dashes, 3 = mingled */
  81.  
  82. /* WIDEFAULT is the assumed output width when we can't measure the window.
  83.    FLACK is char used to mark icon'd files in output.  Not used these days.
  84.    HYPH is used to mark option arguments for mane.
  85.    CSI is the one-char string that starts "escape sequences"; same as esc [.
  86.    MAXCOLS is the max number of text columns to stack listed names in.
  87.    PREPENGTH is the maximum length of pathnames labelling recursive levels.
  88.    LOOPDEPTH is how far back we check for hard-link looping.
  89.    STACKNEEDED is the amount of stack space needed to scan a directory.
  90.    PATLIMIT is the maximum length of pattern strings.
  91.    ABSIZE is the maximum length of paths converted by the -T option.
  92.    SOFTSIZE is the number of characters allowed for a soft link's path string.
  93.    MULTILIMIT is the number of locks that can be handled in a multiple assign.
  94. */
  95.  
  96.  
  97. #include <exec/types.h>
  98. #include <exec/io.h>
  99. #include <exec/memory.h>
  100. #include <dos/dosextens.h>
  101. #include <dos/dostags.h>
  102. #include <dos/datetime.h>
  103. #include <clib/dos_protos.h>
  104. #include <clib/exec_protos.h>
  105. #include <clib/alib_protos.h>
  106. #include <pragmas/dos_lib.h>
  107. #include <pragmas/exec_lib.h>
  108. #include <ctype.h>
  109. #include <string.h>
  110. #include <Paul.h>
  111.  
  112. #ifdef put
  113. #  undef put
  114. #endif
  115.  
  116. /* Here we have a simplified version of <devices/conunit.h> which does not
  117. pull in stuff like struct Window and struct TextFont: */
  118.  
  119. struct ConUnit {
  120.     short pad[21];
  121.     short cu_XMax, cu_YMax;
  122. };
  123. /* pretty simplified, wasn't it */
  124.  
  125.  
  126. typedef struct _fly *flip;
  127.  
  128. typedef struct _fly {
  129.     flip next;            /* list link */
  130.     long length, blox, tection;
  131.     struct DateStamp when;
  132.     long keee;
  133.     str comment;
  134.     short ordination;
  135.     char name[31];
  136.     /* these are the most heavily used flags: */
  137.     char /* bool */ jected, infoed, wanted, dirred, softlink;
  138.     ushort fflags;    /* ... and these are the less used ones */
  139.     str slinky;
  140. } fly;             /* size exactly 80 bytes always */
  141.  
  142. #define F_HARDLINK 1
  143. #define F_DESCEND  2
  144. #define F_ROOT     4
  145.  
  146.  
  147. struct cuont {
  148.     long blok, byt, fil, dir;
  149. };
  150.  
  151.  
  152. adr stacklimit;
  153. str argline;
  154. str *argv;
  155. int arglen, argc, hyphc;
  156.  
  157. short cwid, song, keygits, cols, wid, abort, rdepth, steil = 0;
  158.  
  159. bool color, curse, cize, complete, cron, cons, cutdirs, ceys, cutfils, colorful;
  160. bool /* cage, */ canydepth, csternal, cortless, consumption, ctifle, cweeek;
  161. bool creverse, colsort, cizesort, cursorhide, convertpath, cancelenvar, cupside;
  162. bool zize, zomplete, /* zons, FLACK */ zurse, zutdirs, zutfils, zortless;
  163. bool zuttop, zutbot;
  164.  
  165. long protlook, protwant, before, after, tooday;
  166.  
  167. str oform, xform;
  168.  
  169. struct ConUnit *cuca;
  170. struct Process *me;
  171. long hair;
  172.  
  173. ubyte mesh[PATLIMIT * 2 + 3];
  174. char abspath[ABSIZE];
  175.  
  176. struct AnchorPath *ankh;
  177.  
  178. bool patty, flipat, didaninny, needsnl, anyfiles, morethan1;
  179. bool notadir, notadirbutokay;
  180.  
  181. #ifdef MARKLINKS
  182. bool anylinks;
  183. #endif
  184.  
  185. char prepath[PREPENGTH];
  186.  
  187.  
  188. struct Ants {
  189.     struct Ants *prav;
  190.     long kay;
  191. };
  192.  
  193.  
  194. struct DosLibrary *DOSBase;
  195.  
  196.  
  197. char versionstring[] = "\0$VER: Dr " VERSION " (" __DATE__ ")\n\r";
  198.  
  199. char helpslab[] =
  200. "    -C sort from oldest to newest       -D show only dirs, not files\n"
  201. "    -F show only files, not dirs        -H sort in rows, not columns\n"
  202. "    -I show .info files normally        -K show disk addresses\n"
  203. "    -L show size protection date etc.   -M don't use color to show icons\n"
  204. "    -O one complete pathname per line   -R show contents of all subdirs\n"
  205. "    -S show length of each file         -T convert paths to absolute form\n"
  206. "    -U just show disk space usage       -V sort in reverse order\n"
  207. "    -X show date etc, not contents      -Y use day names for recent dates\n"
  208. "    -Z sort from smallest to largest    -? no output; return 5 if no match\n"
  209. "    -@ don't check DR-OPTS variable     -! turn off cursor for faster output\n"
  210. "    -A# (e.g. -A7) show files and dirs changed in the last # days\n"
  211. "    -B# (e.g. -B30) show those changed more than # days ago\n"
  212. "    -N# (e.g. -N3) select style for arranging output of files and dirs\n"
  213. "    -Pb where b is one or more of H S P A R W E D with optional ~ in front\n"
  214. "         of letters (e.g. -PS~A~D): don't show non-matching protection bits\n"
  215. "    -[...string...] describes format of output -- see docs for description\n"
  216. "         of special codes preceded with \"\\\".\n"
  217. "    -{...string...} is like -[...] but result is executed as a command\n";
  218.  
  219.  
  220. #define SIGMASK (long) (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D)
  221.  
  222.  
  223. ubyte sorder[256] = "\0\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17"
  224.             "\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37"
  225.             " !\"#$%&'()*+,-./0123456789:;<=>?"
  226.             "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
  227.             "`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~\177"
  228.             "\200\201\202\203\204\205\206\207"
  229.             "\210\211\212\213\214\215\216\217"
  230.             "\220\221\222\223\224\225\226\227"
  231.             "\230\231\232\233\234\235\236\237"
  232.             " ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿"
  233.             "AAAAAAACEEEEIIIIDNOOOOO×OUUUUYÞS"
  234.             "AAAAAAACEEEEIIIIDNOOOOO÷OUUUUYÞY";
  235.  
  236. /* for correct sorting: "Mädchen" between "madam" and "madman". */
  237.  
  238.  
  239. struct pack {
  240.     struct FileInfoBlock fibuf;
  241.     struct StandardPacket sp;
  242.     struct MsgPort repp;
  243.     bool isout;
  244.     BPTR lock;
  245. };
  246.  
  247.  
  248. /* ================== functions: ================== */
  249.  
  250.  
  251. #pragma amicall(DOSBase, 0xf0, DoPkt3(d1,d2,d3,d4,d5))
  252.  
  253. #ifdef LEAKAGE
  254.  
  255. import adr AllocYell(long a, long b, str c, long d);
  256. import void FreeYell(adr a, long b, str c, long d);
  257. #define AllocMem(a, b) AllocYell((long) a, (long) b, __FUNC__, (long) __LINE__)
  258. #define FreeMem(a, b) FreeYell(a, (long) b, __FUNC__, (long) __LINE__)
  259. #define _AllocMem(a, b) AllocYell((long) a, (long) b, __FUNC__, (long) __LINE__)
  260. #define _FreeMem(a, b) FreeYell(a, (long) b, __FUNC__, (long) __LINE__)
  261.  
  262. #endif
  263.  
  264.  
  265. import bool OpenSmallIO(void (*ff)());
  266. import void CloseSmallIO(void), puch(ushort c), put(str s),
  267.         putfmt(str format, ...), pflush(void), StopAllOutput(void);
  268.  
  269.  
  270. #define lower(C)  ((C) | 0x20)
  271.  
  272.  
  273. /* for forward references: */
  274.  
  275. void DoInner(str n, BPTR l, bool hasparent,
  276.             struct Ants *ants, struct cuont *great);
  277. void Cough1(register flip y, bool dirs, short *col);
  278. void FloorMat(register flip y, bool xeq);
  279.  
  280.  
  281. private void btoc(str c, ubyte *b, short lim)        /* b == c is okay */
  282. {
  283.     register ushort l = *b;
  284.     if (l > lim) l = lim;
  285.     strncpy(c, (str) b + 1, (size_t) l);
  286.     c[l] = '\0';
  287. }
  288.  
  289.  
  290.  
  291. private void SendExNext(register struct pack *p)
  292. {
  293.     register struct DosPacket *d = &p->sp.sp_Pkt;
  294.     register short l = strlen(p->fibuf.fib_FileName);
  295.     register str s = &p->fibuf.fib_FileName[l];
  296.  
  297.     while (s > &p->fibuf.fib_FileName[0])    /* un-btoc() */
  298.     *s = s[-1], s--;
  299.     *(ubyte *) s = l;
  300.     /* the filesystem ralphs if you send a null lock to pr_FileSystemTask? */
  301.     p->sp.sp_Msg.mn_Node.ln_Name = (adr) d;
  302.     d->dp_Link = &p->sp.sp_Msg;
  303.     d->dp_Port = p->sp.sp_Msg.mn_ReplyPort = &p->repp;
  304.     d->dp_Type = ACTION_EXAMINE_NEXT;
  305.     d->dp_Arg1 = p->lock;
  306.     d->dp_Arg2 = ((long) &p->fibuf) >> 2;
  307.     p->isout = true;
  308.     PutMsg(bip(struct FileLock, p->lock)->fl_Task, d->dp_Link);
  309. }
  310.  
  311.  
  312.  
  313. private void WaitExNext(struct pack *p)
  314. {
  315.     register struct Message *m;
  316.     while (p->isout) {
  317.     WaitPort(&p->repp);
  318.     while (m = GetMsg(&p->repp))
  319.         if (m == &p->sp.sp_Msg) {
  320.         p->isout = false;
  321.         break;
  322.         }
  323.     }
  324.     btoc(p->fibuf.fib_FileName, (ubyte *) p->fibuf.fib_FileName, 30);
  325.     btoc(p->fibuf.fib_Comment, (ubyte *) p->fibuf.fib_Comment, 79);
  326. }
  327.  
  328.  
  329.  
  330. void AsExCleanup(struct pack **pk)
  331. {
  332.     struct pack *p = *pk;
  333.  
  334.     if (p && ~(long) p) {
  335.     WaitExNext(p);            /* you can't AbortIO a packet */
  336.     FREE(p);
  337.     *pk = (adr) ~0;
  338.     }
  339. }
  340.  
  341.  
  342.  
  343. long AsExamine(BPTR lok, struct FileInfoBlock *fibb, struct pack **pk)
  344. {
  345.     long r;
  346.  
  347.     *pk = null;
  348.     r = Examine(lok, fibb);
  349.     if (!lok || !r || fibb->fib_EntryType < 0)
  350.     return r;
  351.     if (NEWPZ(*pk)) {
  352.     register struct pack *p = *pk;
  353.     p->repp.mp_Node.ln_Type = NT_MSGPORT;
  354.     p->repp.mp_Flags = PA_SIGNAL;
  355.     p->repp.mp_SigBit = SIGB_DOS;
  356.     p->repp.mp_SigTask = me;
  357.     NewList(&p->repp.mp_MsgList);
  358.     p->repp.mp_MsgList.lh_Type = NT_MSGPORT;    /* what the heck */
  359.     p->fibuf = *fibb;        /* copy of struct, not pointer */
  360.     p->isout = false;
  361.     p->lock = lok;
  362.     SendExNext(p);
  363.     }
  364.     return r;
  365. }
  366.  
  367.  
  368.  
  369. long AsExNext(BPTR lok, struct FileInfoBlock *fibb, struct pack **pk)
  370. {
  371.     long ret;
  372.     struct pack *p = *pk;
  373.  
  374.     if (!~(long) p) {
  375.     me->pr_Result2 = ERROR_NO_MORE_ENTRIES;
  376.     return 0;
  377.     }
  378.     if (!p)
  379.     return ExNext(lok, fibb);
  380.     WaitExNext(p);
  381.     *fibb = p->fibuf;
  382.     ret = p->sp.sp_Pkt.dp_Res1;
  383.     me->pr_Result2 = p->sp.sp_Pkt.dp_Res2;
  384.     if (!ret && me->pr_Result2 == ERROR_NO_MORE_ENTRIES)
  385.     AsExCleanup(pk);
  386.     else
  387.     SendExNext(p);        /* continue in spite of error */
  388.     return ret;
  389. }
  390.  
  391.  
  392.  
  393. void putn(str s)
  394. {
  395.     put(s);
  396.     puch('\n');
  397. }
  398.  
  399.  
  400.  
  401. long StackLeft(void)
  402. {
  403.     short i;
  404.     return (long) &i + 14 - (long) stacklimit;
  405. }
  406.  
  407.  
  408.  
  409. short digits(register ulong l)
  410. {
  411.     register short d = 0;
  412.     while (l) {
  413.     d++;
  414.     l /= 10;
  415.     }
  416.     return (d ? d : 1);
  417. }
  418.  
  419.  
  420.  
  421. void pad(short w)
  422. {
  423.     while (--w >= 0)
  424.     puch(' ');
  425. }
  426.  
  427.  
  428.  
  429. void padong(ulong n, short w)
  430. {
  431.     pad(w - digits(n));             /* RawDoFmt doesn't have %*ld */
  432.     putfmt("%ld", n);
  433. }
  434. /* RawDoFmt doesn't have %lu either, so let's just hope n is always positive */
  435.  
  436.  
  437.  
  438. void fortection(str s, register ulong bits)
  439. {
  440.     register short b;
  441.  
  442.     bits ^= 15;
  443.     strcpy(s, "hsparwed");
  444.     for (b = 0; b <= 7; b++)
  445.     if (!(bits & bit(b)))
  446.         s[7 - b] = '-';
  447.     s[8] = 0;
  448. }
  449.  
  450.  
  451.  
  452. #asm
  453.     public    _mob
  454.  
  455. _mob:    move.b    d0,(a3)+
  456.     rts
  457. #endasm
  458.  
  459.  
  460.  
  461. void formdate(str s, struct DateStamp *when, short space)
  462. {
  463.     char temp[20];
  464.     struct DateTime dt;
  465.  
  466.     dt.dat_Stamp = *when;
  467.     dt.dat_Format = FORMAT_DOS;        /* FORMAT_DEF ? */
  468.     dt.dat_Flags = cweeek ? DTF_SUBST : 0;
  469.     dt.dat_StrDay = null;
  470.     dt.dat_StrDate = temp;
  471.     dt.dat_StrTime = temp + 10;        /* adjust this if FORMAT_other */
  472.     DateToStr(&dt);
  473.     temp[9] = ' ';
  474.     temp[18] = 0;
  475.     while (space > 18)
  476.     *s++ = ' ', space--;
  477.     strncpy(s, temp, space);
  478.     s[space] = 0;
  479. }
  480.  
  481.  
  482.  
  483. /* Another personal ad found in EXPRESS "The East Bay's Free Weekly":
  484.     HI.
  485.    Yup, that was the whole ad.  Right after it:
  486.     RALPH, a 1967 Cadillac, now accepting devotees.
  487. */
  488.  
  489.  
  490.  
  491. void Lose(register flip f)
  492. {
  493.     if (f) {
  494.     if (f->comment)
  495.         FreeMem(f->comment, 80L);
  496.     if (f->slinky && f->softlink > 0)
  497.         FreeMem(f->slinky, SOFTSIZE);
  498.     FREE(f);
  499.     }
  500. }
  501.  
  502.  
  503.  
  504. void CCch(void)
  505. {
  506.     if (abort < 5 && (SetSignal(0L, SIGMASK) & SIGMASK)) {
  507.     abort = 5;
  508.     StopAllOutput();
  509.     Write(Output(), "\n *** BREAK\n", 12L);
  510.     me->pr_Result2 = 0;
  511.     }
  512. }
  513.  
  514.  
  515.  
  516. void PFault(str head)
  517. {
  518.     pflush();
  519.     if (hair == ERROR_TOO_MANY_LEVELS)
  520.     putfmt("%s: not enough stack space.\n", head);
  521.     else
  522.     PrintFault(hair, head);
  523. }
  524.  
  525.  
  526.  
  527. flip /* flop & */ Fly(register struct FileInfoBlock *b)
  528. {
  529.     register flip z;
  530.     register short tt = b->fib_DirEntryType;
  531.  
  532.     if (!NEW(z))
  533.     return null;
  534.     z->comment = null;            /* if alloc fails, no big deal */
  535.     if (*b->fib_Comment && (z->comment = Alloc(80)))
  536.     strcpy(z->comment, b->fib_Comment);
  537.     z->next = null;
  538.     z->length = b->fib_Size;
  539.     z->blox = b->fib_NumBlocks;
  540.     z->tection = b->fib_Protection;
  541.     z->when = b->fib_Date;
  542.     z->keee = b->fib_DiskKey;
  543.     strcpy(z->name, b->fib_FileName);
  544.     z->fflags = (tt == 4 || tt == -4 ? F_HARDLINK : 0);
  545.     z->softlink = tt == 3;
  546.     z->dirred = tt > 0;
  547.     z->wanted = z->jected = z->infoed = false;
  548.     z->slinky = null;
  549.     return z;
  550. }
  551.  
  552.  
  553.  
  554. void DoImmediate(flip m, bool immediacy)
  555. {
  556.     if (!m->jected) {
  557.     if (xform)
  558.         FloorMat(m, true);
  559.     if (immediacy)
  560.         if (oform && !ctifle)
  561.         FloorMat(m, false);
  562.         else {
  563.         short fakecol = 0;
  564.         Cough1(m, m->dirred, &fakecol);
  565.         }
  566.     }
  567. }
  568.  
  569.  
  570.  
  571. flip Scan1(short *ficou, struct FileInfoBlock *deef, BPTR deer, bool noparent)
  572. {
  573.     flip result = null;
  574.     BPTR dp, ocd, fo;
  575.     char fone[31];
  576.  
  577.     if (!(result = Fly(deef)))
  578.     return null;
  579.     *ficou = 1;            /* we're seeing if it has a .info file */
  580.     dp = ParentDir(deer);
  581.     if ((!cons || !colorful) && strlen(deef->fib_FileName) < 26 && dp) {
  582.     ocd = CurrentDir(dp);
  583.     strcpy(fone, result->name);
  584.     strcat(fone, ".info");
  585.     CCch();
  586.     if (!abort && (fo = RLock(fone))) {
  587.         result->infoed = true;
  588.         UnLock(fo);
  589.     }
  590.     CurrentDir(ocd);
  591.     }
  592.     /* WE SEEM to be running into an undocumented feature here...  the
  593.     /* ding bling ParentDir function sometimes sets IoErr = 212 after a
  594.     /* perfectly normal and SUCCESSFUL call.  When it does this, the
  595.     /* following RLock also does so.  So we just band-aid it: */
  596.     if (me->pr_Result2 == ERROR_OBJECT_NOT_FOUND
  597.             || me->pr_Result2 == ERROR_OBJECT_WRONG_TYPE)
  598.     me->pr_Result2 = 0;
  599.     if (dp)
  600.     UnLock(dp);
  601.     else {
  602.     result->fflags |= F_ROOT;
  603.     result->length = result->tection = 0;        /* invalid bits */
  604.     }
  605.     if (noparent) {
  606.     zurse = zutfils = zutdirs = false;
  607.     if (!oform && !zortless)
  608.         zomplete = true;
  609.     notadirbutokay = true;
  610.     if (patty)
  611.         result->jected = notadir = true;
  612.     }
  613.     DoImmediate(result, zortless);
  614.     return result;
  615. }
  616.  
  617.  
  618.  
  619. bool OutOfDate(flip f)
  620. {
  621.     long a;
  622.     struct DateStamp d;
  623.  
  624.     if (!(before | after))
  625.     return false;
  626.     if (!tooday) {
  627.     DateStamp((adr) &d);
  628.     tooday = d.ds_Days;
  629.     }
  630.     a = 1 + tooday - f->when.ds_Days;
  631.     if (a <= 0) a = 1;
  632.     if (before > after)
  633.     return a > after && a <= before;
  634.     else
  635.     return a > after || a <= before;
  636. }
  637.  
  638.  
  639.  
  640. void Descendify(flip f, BPTR deer, struct Ants *pan, struct cuont *great)
  641. {
  642.     BPTR ocd = CurrentDir(deer), innerdeer;
  643.     if (innerdeer = RLock(f->name)) {
  644.     register bool p = patty;
  645.     if (!canydepth)
  646.         patty = false;
  647.     rdepth++;
  648.     DoInner(f->name, innerdeer, true, pan, great);
  649.     rdepth--;
  650.     patty = p;
  651.     UnLock(innerdeer);
  652.     } else {
  653.     hair = me->pr_Result2;
  654.     putfmt(" *** Can't lock inner directory \"%s\"!\n", f->name);
  655.     }
  656.     CurrentDir(ocd);
  657. }
  658.  
  659.  
  660.  
  661. flip ScanInside(short *ficou, struct FileInfoBlock *deef, BPTR deer,
  662.         struct Ants *pan, struct cuont *great, adr *potholderptr)
  663. {
  664.     flip result = null, more;
  665.     long air = 0;
  666.     bool prejected, plork;
  667.     /* This is for avoiding explosions with ReadLink: */
  668.     struct DeviceList *vol = gbip(bip(struct FileLock, deer)->fl_Volume);
  669.     bool bogondisk = (vol->dl_DiskType & ~2L) == 0x444F5300;
  670.  
  671.             /* vvvv prevent multiple "please replace" in -R */
  672.     while (hair != ERROR_DEVICE_NOT_MOUNTED
  673.                 && AsExNext(deer, deef, potholderptr)) {
  674.     CCch();
  675.     if (abort || !(more = Fly(deef))) break;
  676.     prejected = (more->dirred ? zutdirs : zutfils)
  677.             || (more->tection & protlook) != protwant
  678.             || OutOfDate(more);
  679.     plork = (!prejected || (curse && more->dirred && !canydepth))
  680.             && (!patty || MatchPatternNoCase(mesh, more->name));
  681.     more->jected = prejected || !plork;
  682.     if ((plork || canydepth) && zurse && more->dirred && !more->softlink) {
  683.         more->fflags |= F_DESCEND;
  684.         if (!cupside)
  685.         Descendify(more, deer, pan, great);
  686.     }
  687.     if (more->softlink && zomplete)
  688.         if (!bogondisk && (more->slinky = Alloc(SOFTSIZE))) {
  689.         bool r = ReadLink(bip(struct FileLock, deer)->fl_Task,
  690.                   deer, more->name, more->slinky, SOFTSIZE);
  691.         if (!r) {
  692.             FreeMem(more->slinky, SOFTSIZE);
  693.             more->slinky = (str) - me->pr_Result2;
  694.             more->softlink = -1;
  695.         }
  696.         }
  697.     DoImmediate(more, zortless);
  698.     more->next = result;
  699.     result = more;
  700.     (*ficou)++;
  701.     }
  702.     if (air)
  703.     me->pr_Result2 = air;
  704.     if (me->pr_Result2 == ERROR_NO_MORE_ENTRIES)
  705.     me->pr_Result2 = 0;
  706.     return result;
  707. }
  708.  
  709.  
  710.  
  711. /* scans a Dos directory and returns the findings in a linked list of fly's.
  712.    *ficou tells the number of elements in the list. */
  713.  
  714. flip ScanDeer(BPTR deer, short *ficou, bool hasparent,
  715.             struct Ants *pan, struct cuont *great)
  716. {
  717.     flip result = null, more;
  718.     register struct FileInfoBlock *deef;
  719.     adr potholder;
  720.  
  721.     *ficou = 0;
  722.     if (!NEWP(deef)) {
  723.     me->pr_Result2 = ERROR_NO_FREE_STORE;
  724.     return null;
  725.     }
  726.     if (AsExamine(deer, deef, &potholder)) {
  727.     CCch();
  728.     if (!abort)
  729.         if (deef->fib_DirEntryType < 0 || (csternal
  730.                 && !patty && !zurse && !consumption))
  731.         result = Scan1(ficou, deef, deer, !hasparent);
  732.         else
  733.         result = ScanInside(ficou, deef, deer, pan, great, &potholder);
  734.     }
  735. #ifdef DEBUG
  736.     if (me->pr_Result2 == -1)
  737.         me->pr_Result2 = 0;        /* work around SDB shortcoming */
  738. #endif
  739.     if (me->pr_Result2 && !abort) {
  740.     hair = me->pr_Result2;
  741.     if (!ctifle)
  742.         PFault("DR COULDN'T FINISH SCAN");
  743.     CCch();
  744.     }
  745.     AsExCleanup(&potholder);
  746.     FREE(deef);
  747.     if (abort)
  748.     while (result) {
  749.         more = result;
  750.         result = result->next;
  751.         Lose(more);
  752.     }
  753.     return result;
  754. }
  755.  
  756.  
  757. void inatl_strupr(register ustr pp)
  758. {
  759.     register ubyte c;
  760.     for ( ; c = *pp; pp++)
  761.     if ((c >= 'a' && c <= 'z') || (c >= 0xE0 && c != 0xF7 && c != 0xFF))
  762.         *pp = c - 32;
  763. }
  764.  
  765.  
  766.  
  767. #ifdef C_NOT_ASM        /* redo these in assembly for speed */
  768.  
  769. short alpha(ubyte *a, ubyte *b)
  770. {
  771.     register ubyte ac, bc;
  772.     short res, lean = 0;
  773.     do {
  774.     if (!lean)
  775.         lean = (short) *a - (short) *b;
  776.     ac = sorder[*(a++)];
  777.     bc = sorder[*(b++)];
  778.     } while (ac && ac == bc);
  779.     res = (short) ac - (short) bc;
  780.     return res ? res : lean;
  781. }
  782.  
  783.  
  784.  
  785. short alpo(flip a, flip b)
  786. {
  787.     return alpha(a->name, b->name);
  788. }
  789.  
  790.  
  791.  
  792. short olda(flip a, flip b)
  793. {
  794.     register long t;
  795.     if (t = a->when.ds_Days - b->when.ds_Days)
  796.     return t;
  797.     if (t = a->when.ds_Minute - b->when.ds_Minute)
  798.     return t;
  799.     return a->when.ds_Tick - b->when.ds_Tick;
  800. }
  801.  
  802.  
  803.  
  804. short siez(flip a, flip b)
  805. {
  806.     if (a->length < b->length)
  807.     return -1;
  808.     else
  809.     return a->length > b->length;
  810. }
  811.  
  812.  
  813.  
  814. short infoo(ubyte *a, ubyte *b)
  815. /* returns 0 if filename b is the .info of filename a, positive if b is
  816.    alphabetically after a's .info name, negative if before. */
  817. {
  818.     ubyte acat[36];
  819.     register ubyte ac, bc;
  820.     register short f = (short) sorder[b] - (short) sorder[a];
  821.     if (f)
  822.     return f;
  823.     strcpy(acat, a);
  824.     strcat(acat, ".info");
  825.     a = acat;
  826.     do {
  827.     ac = sorder[*(a++)];
  828.     bc = sorder[*(b++)];
  829.     } while (ac && ac == bc);
  830.     return (short) bc - (short) ac;
  831. }
  832.  
  833. #else
  834.  
  835. #pragma regcall(alpo(a0, a1))
  836. #pragma regcall(olda(a0, a1))
  837. #pragma regcall(siez(a0, a1))
  838. #pragma regcall(alpha(a0, a1))
  839. #pragma regcall(infoo(a1, a0))
  840.         /*    ^^  ^^    backwards on purpose */
  841.  
  842. /* these optimized versions make a noticeable difference when sorting a
  843. directory with 75 files or more */
  844.  
  845. #asm
  846.     public        _alpha
  847.     public        _alpo
  848.     public        _olda
  849.     public        _siez
  850.     public        _infoo
  851.  
  852.  
  853. _alpo:    lea        38(a0),a0        ; first->name
  854.     lea        38(a1),a1        ; second->name
  855.     ; fall thru:
  856.  
  857. _alpha:    movem.l        a2/d2,-(sp)
  858.     lea        _sorder,a2
  859.     moveq        #0,d1
  860.     moveq        #0,d2
  861. nxt:      moveq        #0,d0
  862.       move.b    (a0),d0
  863.       swap        d0
  864.       move.b    (a0)+,d0        ; get two copies
  865.       move.b    (a1)+,d1
  866.       beq        out            ; end of string
  867.       sub.w        d1,d0
  868.       beq        nxt            ; exactly equal chars
  869.       tst.w        d2
  870.       bne        leanin            ; diff of unsordered bytes
  871.         move.w    d0,d2            ; if no such already found
  872. leanin:      swap        d0            ; restore original byte
  873.       move.b    (a2,d0.w),d0        ; convert by sorder
  874.       move.b    (a2,d1.w),d1
  875.       cmp.b        d0,d1
  876.       beq        nxt            ; equal after sorder conversion
  877. out:    sub.w        d1,d0            ; compare as unsigned bytes
  878.     bne        unlean
  879.       move.w    d2,d0            ; use lean if otherwise equal
  880. unlean:    movem.l        (sp)+,a2/d2
  881.     rts
  882.  
  883.  
  884. _olda:    move.l        16(a0),d0        ; a->when.ds_Days
  885.     sub.l        16(a1),d0        ; - b->when.ds_Days
  886.     bne        zulte
  887.     move.l        20(a0),d0        ; a->when.ds_Minute
  888.     sub.l        20(a1),d0        ; - b->when.ds_Minute
  889.     bne        zulte
  890.     move.l        24(a0),d0        ; a->when.ds_Tick
  891.     sub.l        24(a1),d0        ; - b->when.ds_Tick
  892. zulte:    rts
  893.  
  894.  
  895. _siez:    move.l        4(a0),d0        ; a->length
  896.     sub.l        4(a1),d0        ; - b->length
  897.     bge        nnegrt
  898.       moveq        #-1,d0
  899.       rts
  900. nnegrt:    bne        posrt
  901.     moveq        #0,d0
  902.     rts
  903. posrt:    moveq        #1,d0
  904.     rts
  905.  
  906.  
  907. _infoo:    move.l        a2,-(sp)
  908.     moveq        #0,d0
  909.     moveq        #0,d1
  910.     move.b        (a0),d0            ; "b"
  911.     move.b        (a1),d1            ; "a"
  912.     lea        _sorder,a2
  913.     move.b        0(a2,d0.w),d0
  914.     move.b        0(a2,d1.w),d1
  915.     sub.b        d1,d0            ; quick pre-test
  916.     beq        check
  917.       ext.w        d0            ; return test diff if nonzero
  918.       move.l    (sp)+,a2
  919.       rts                    
  920. check:    link        a5,#-40            ; the real test
  921.     move.l        sp,a2            ; temporary copy area
  922. cpy:      move.b    (a1)+,(a2)+
  923.       bne        cpy
  924.     move.b        #'.',-1(a2)        ; overwrite final nul
  925.     move.b        #'i',(a2)+
  926.     move.b        #'n',(a2)+
  927.     move.b        #'f',(a2)+
  928.     move.b        #'o',(a2)+
  929.     clr.b        (a2)
  930.     move.l        sp,a1
  931.     lea        _sorder,a2
  932. loeup:      move.b    (a0)+,d0
  933.       move.b    (a1)+,d1
  934.       beq        pueol
  935.       move.b    (a2,d0.w),d0
  936.       move.b    (a2,d1.w),d1
  937.       cmp.b        d1,d0
  938.       beq        loeup
  939. pueol:    sub.w        d1,d0
  940.     unlk        a5
  941.     move.l        (sp)+,a2
  942.     rts
  943.  
  944. #endasm
  945.  
  946. import short alpo(flip a, flip b), olda(flip a, flip b), siez(flip a, flip b);
  947. import short alpha(ubyte *a, ubyte *b);
  948. import short infoo(ubyte *a, ubyte *b);
  949.  
  950. #endif
  951.  
  952.  
  953.  
  954. #ifdef C_NOT_ASM
  955.  
  956. flip MergeSort(flip flist, short ficou, bool reverse,
  957.         short (*sortie)(flip a, flip b), short (*second)(flip a, flip b))
  958. {
  959.     register flip l, l1, l2;
  960.     flip *p;
  961.     register short i, oint, s;
  962.  
  963.     if (ficou <= 2) {                /* handle degenerate cases */
  964.     if (ficou < 2) return flist;
  965.     l = flist->next;
  966.     if (!(s = sortie(flist, l)))
  967.         s = second(flist, l);
  968.     if ((s > 0) ^ reverse) {
  969.         flist->next = null;
  970.         l->next = flist;
  971.         flist = l;
  972.     }
  973.     return flist;
  974.     }
  975.     oint = ficou >> 1;                /* split list in two */
  976.     l1 = l = flist;
  977.     for (i = 1; i < oint; i++)
  978.     l = l->next;
  979.     l2 = l->next;
  980.     l->next = null;
  981.     l1 = MergeSort(l1, oint, reverse, sortie, second);    /* sort each half */
  982.     l2 = MergeSort(l2, ficou - oint, reverse, sortie, second);
  983.     p = &flist;                        /* merge sublists */
  984.     while (l1 && l2) {
  985.     if (!(s = sortie(l1, l2)))
  986.         s = second(l1, l2);
  987.     if ((s < 0) ^ reverse)
  988.         l = l1, l1 = l1->next;
  989.     else
  990.         l = l2, l2 = l2->next;
  991.     *p = l;
  992.     p = &l->next;
  993.     }
  994.     if (l1)
  995.     *p = l1;
  996.     else
  997.     *p = l2;
  998.     return flist;
  999. }
  1000.  
  1001. #else
  1002.  
  1003. #asm
  1004.         public        _MergeSort
  1005.  
  1006. regzz        reg        d2-d7/a2-a6
  1007.  
  1008. _MergeSort:    movem.l        regzz,-(sp)
  1009.         move.l        a0,a5        ; flist
  1010.         move.l        a1,a6        ; sortie
  1011.         move.l        a2,d5        ; second
  1012.         cmp.w        #2,d2        ; ficou
  1013.         bgt        split
  1014.           bne        retflist    ; list length 1, return it
  1015.           move.l    (a5),a2        ; length 2, check order
  1016.           move.l    a2,a1        ; ^^ l = flist->next
  1017.           move.l    a5,a0
  1018.           jsr        (a6)        ; s = sortie(flist, l)
  1019.           tst.w        d0
  1020.           bne        nosecond    ; no difference?
  1021.             move.l    a2,a1
  1022.             move.l    a5,a0
  1023.             exg        d5,a6
  1024.             jsr        (a6)        ; s = second(flist, l)
  1025.             exg        a6,d5
  1026. nosecond:      tst.w        d6        ; if (reverse) s = -s
  1027.           beq        noneg1
  1028.             neg.w    d0
  1029. noneg1:          tst.w        d0
  1030.           ble        retflist    ; is in correct order
  1031.           clr.l        (a5)        ; flist->next = null
  1032.           move.l    a5,(a2)        ; l->next = flist
  1033.           move.l    a2,a5        ; flist = l
  1034.           bra        retflist
  1035.  
  1036. split:        move.w        d2,d4        ; save for later
  1037.         move.w        d2,d3
  1038.         asr.w        #1,d3        ; oint = ficou / 2
  1039.         move.l        a5,a2        ; l = flist
  1040.         move.l        a5,a3        ; l1 = flist
  1041.         move.w        d3,d0
  1042.         subq        #1,d0
  1043. zoop:          dbra        d0,zinn        ; do oint-1 times
  1044.             bra        zun        ; yes we can go through 0 times
  1045. zinn:          move.l    (a2),a2        ; l = l->next
  1046.           bra        zoop
  1047. zun:        move.l        (a2),d7        ; l2 = l->next
  1048.         clr.l        (a2)        ; l->next = null
  1049.  
  1050.         move.l        a2,-(sp)    ; save
  1051.         move.l        d5,a2        ; second
  1052.         move.l        a6,a1        ; sortie
  1053.         move.w        d3,d2        ; oint
  1054.         move.l        a3,a0        ; l1
  1055.         bsr        _MergeSort    ; l1 = MergeSort(l1, oint,
  1056.         move.l        d0,a3        ;        reverse, sortie, second)
  1057.         move.l        a6,a1        ; sortie again
  1058.         move.w        d4,d2
  1059.         sub.w        d3,d2        ; ficou - oint
  1060.         move.l        d7,a0        ; l2
  1061.         bsr        _MergeSort    ; l2 = MergeSort(l2, ficou - oint,
  1062.         move.l        d0,d7        ;        reverse, sortie, second)
  1063.         move.l        (sp)+,a2    ; restore
  1064.  
  1065.         move.l        a5,-(sp)    ; put flist in memory
  1066.         move.l        sp,a5        ; p = &flist
  1067. elihu:          move.l    a3,d0        ; pseudo tst.l
  1068.           beq        finnish
  1069.           move.l    d7,d0
  1070.           beq        finnish        ; neither list empty?
  1071.           move.l    d7,a1
  1072.           move.l    a3,a0
  1073.           jsr        (a6)        ; s = sortie(l1, l2)
  1074.           tst.w        d0
  1075.           bne        nosecond2
  1076.             move.l    d7,a1
  1077.             move.l    a3,a0
  1078.             exg        d5,a6
  1079.             jsr        (a6)        ; s = second(l1, l2)
  1080.             exg        a6,d5
  1081. nosecond2:      tst.w        d6
  1082.           beq        noneg2
  1083.             neg.w    d0
  1084. noneg2:          tst.w        d0
  1085.           bge        twooo
  1086.             move.l    a3,a2        ; l = l1
  1087.             move.l    (a3),a3        ; l1 = l1->next
  1088.           bra        onnne
  1089. twooo:            move.l    d7,a2        ; l = l2
  1090.             exg        a0,d7
  1091.             move.l    (a0),a0        ; l2 = l2->next
  1092.             exg        a0,d7
  1093. onnne:          move.l    a2,(a5)        ; *p = l
  1094.           move.l    a2,a5        ; p = &l->next
  1095.           bra        elihu
  1096. finnish:    move.l        a3,d0        ; is l1 empty?
  1097.         beq        tuuue
  1098.           move.l    a3,(a5)        ; *p = l1
  1099.         bra        retstarp
  1100. tuuue:          move.l    d7,(a5)        ; *p = l2
  1101. retstarp:    move.l        (sp)+,a5
  1102.  
  1103. retflist:    move.l        a5,d0
  1104.         movem.l        (sp)+,regzz
  1105.         rts
  1106.  
  1107. #endasm
  1108.  
  1109. import flip MergeSort(flip flist, short ficou, bool reverse,
  1110.         short (*sortie)(flip a, flip b), short (*second)(flip a, flip b));
  1111.  
  1112. #pragma regcall(MergeSort(a0, d2, d6, a1, a2))
  1113.  
  1114. #endif
  1115.  
  1116.  
  1117.  
  1118. flip Soart(flip flist, short ficou)
  1119. {
  1120.     register flip t, tt;
  1121.     register short k;
  1122.  
  1123.     if (!flist)
  1124.     return null;
  1125.     if (!cons) {
  1126. /* alpha sorting greatly speeds the following .info rejector, enough to
  1127. justify doing an alpha sort and then doing a different sort afterwards: */
  1128.     flist = MergeSort(flist, ficou, false, alpo, siez);
  1129.     for (t = flist; t; t = t->next)
  1130.         for (tt = t->next; tt; tt = tt->next)
  1131.         if (!(t->jected | tt->dirred))
  1132.             if (!(k = infoo((ubyte *) t->name, (ubyte *) tt->name))) {
  1133.             t->infoed = tt->jected = true;
  1134.             break;
  1135.             } else if (k > 0)
  1136.             break;
  1137.     }
  1138.     if (cizesort)
  1139.     flist = MergeSort(flist, ficou, creverse, siez, (cron ? olda : alpo));
  1140.     else if (cron)
  1141.     flist = MergeSort(flist, ficou, creverse, olda, alpo);
  1142.     else if (!cons || creverse)
  1143.     flist = MergeSort(flist, ficou, creverse, alpo, siez);
  1144.     return flist;
  1145. }
  1146.  
  1147.  
  1148.  
  1149. short CheckWindowWidth(void)
  1150. {
  1151.     struct InfoData *ind;
  1152.     adr cont = bip(struct FileHandle, me->pr_COS)->fh_Type;
  1153.  
  1154.     if (!cuca)
  1155.     if (color && cont && NEWP(ind)) {
  1156.         if (DoPkt1(cont, (long) ACTION_DISK_INFO, (ulong) ind >> 2))
  1157.         cuca = (adr) ((struct IOStdReq *) ind->id_InUse)->io_Unit;
  1158.         else color = false;
  1159.         FREE(ind);
  1160.     } else color = false;
  1161.     return color ? cuca->cu_XMax + 2 : WIDEFAULT + 1;
  1162. }
  1163. /* I know, there's an escape sequence.  But it don't work without you does
  1164. set_raw, which does dos_packet, and it comes out smaller this way.  I could
  1165. probably save 100 bytes or so under 1.3 by doing my own packet handling ... */
  1166.  
  1167. /* we actually return the number of characters that will fit plus one.  This is
  1168. because most calculations relating to window fit have to subtract one to
  1169. compensate for the lack of a one space pad after the last column.  So it saves
  1170. some arithmetic to add the one first. */
  1171.  
  1172.  
  1173.  
  1174. void Columnate(flip flist, short ficou)
  1175. {
  1176.     short n, c = 0;
  1177.     bool filongest = false;
  1178.     register flip f;
  1179. /*  bool oneinfo = false;    FLACK */
  1180.  
  1181.     song = keygits = 1;
  1182.     for (f = flist; f; f = f->next)
  1183.     if (!f->jected) {
  1184.         c++;
  1185.         if ((n = digits(f->length)) > song)
  1186.         song = n;
  1187.         if (ceys && (n = digits(f->keee)) > keygits)
  1188.         keygits = n;
  1189. #ifdef MARKLINKS
  1190.         if ((f->fflags & F_HARDLINK) || f->softlink)
  1191.         anylinks = true;
  1192. #endif
  1193.         if (!f->dirred)
  1194.         anyfiles = true;
  1195.     }
  1196.     if (consumption || oform || zortless)
  1197.     return;
  1198.     cwid = 1;
  1199.     if (!wid)
  1200.     wid = CheckWindowWidth();
  1201.     for (f = flist; f; f = f->next)
  1202.     if (!f->jected) {
  1203.         n = strlen(f->name) + 1 /* 2 - cons */ ;        /* FLACK */
  1204.         if (f->dirred && !f->softlink)
  1205.         n++;
  1206.         if (zize & anyfiles)
  1207.         n += song + 1;
  1208.         if (ceys)
  1209.         n += keygits + 3;
  1210.         if (n >= cwid) {
  1211.         if (!steil && !f->dirred || n > cwid)
  1212.             filongest = !f->dirred;
  1213.         cwid = n;
  1214.         }
  1215. /*        if (f->infoed)
  1216.         oneinfo = true;    FLACK */
  1217.     }
  1218. /*  zons = cons;
  1219.     if (!(cons | oneinfo)) {
  1220.     zons = true;
  1221.     if (!--cwid) cwid = 1;
  1222.     }                FLACK */
  1223.     if (!zomplete) {
  1224.     short kolz;
  1225.     if (!steil) {            /* dented */
  1226.         if (zize)
  1227.         n = (short) !filongest;
  1228.         else
  1229.         n = - (short) filongest;
  1230.         cols = (wid + n) / cwid;
  1231.     } else
  1232.         cols = wid / cwid;
  1233.     if (cols > MAXCOLS) cols = MAXCOLS;
  1234. /*    if (cols > ficou) cols = ficou; */ /* on second thought, nah */
  1235.     if (!cols) cols = 1;         /* window too narrow */
  1236.     kolz = (cols > c ? cols : c);
  1237. /***    cwid = wid / cols; **/         /* share extra space evenly? NAH */
  1238.     if (wid / kolz > cwid)         /* add at most 1 space */
  1239.         cwid++;
  1240. #ifdef OLD_LWID_STUFF    /* stretch to use whole window width */
  1241.     } else {
  1242.     n = wid - /*** (ceys ? 31 + keygits : 28) ***/ 28;
  1243.     if (cwid > n)
  1244.         cwid = n;            /* not enough space */
  1245.     if (n > LWID)
  1246.         n = LWID;            /* use at least LWID if possible */
  1247.     if (n > cwid)
  1248.         cwid = n;
  1249. #endif
  1250.     }
  1251. }
  1252.  
  1253.  
  1254.  
  1255. #ifdef SOMEDAY_MAYBE
  1256.  
  1257. here are some rough notes for an algorithm for doing ultra-compact columns.
  1258. note that this does not take into account the separation of files from
  1259. directories, or other such details.
  1260.  
  1261. declared in g:  short ciwd[MAXCOLS], song[MAXCOLS], keygits[MAXCOLS];
  1262.  
  1263. call columnate in a loop like this:
  1264.     for (cols = MAXCOLS; !Columnate(flist, ficou; cols--) ;
  1265.  
  1266. bool Columnate(flip flist, short ficou)
  1267. {
  1268.     short rose = (ficou + cols - 1) / cols, jag = ficou % cols;
  1269.     short col, row, ord, clot = 0;
  1270.     ... plus most of the variables it already has
  1271.  
  1272.     if (!jag) jag = cols;
  1273.     for (col = 0; col < cols; col++) {
  1274.     if (colsort) {
  1275.         ord = col * rose;
  1276.         if (col > jag) ord -= col - jag;
  1277.     } else ord = col;
  1278.     for (row = 0; ord < ficou; row++) {
  1279.         y = ... the ord'th member of flist
  1280.         ... measure length of name etc. as above, but assign results to
  1281.         ... cwid[col] and song[col] and keygits[col]
  1282.         y->ordination = ord;
  1283.         if (colsort) ord++;
  1284.         else ord += cols;
  1285.     }
  1286.     clot += cwid[col];        /* total page width used so far */
  1287.     if (clot > wid && cols > 1)
  1288.         return false;            /* fail, try fewer cols */
  1289.     }
  1290.     return true;
  1291. }
  1292.  
  1293. #endif
  1294.  
  1295.  
  1296.  
  1297. str Joyn(str bp, str bs, str add)
  1298. {
  1299.     size_t l = strlen(add);
  1300.     if (l > 255 - (bp - bs))
  1301.     return null;
  1302.     strcpy(bp, add);
  1303.     bp += l;
  1304.     return bp;
  1305. }
  1306.  
  1307.  
  1308.  
  1309. void FloorMat(flip y, bool kseeq)
  1310. {
  1311.     char bluff[258], c;
  1312.     ubyte cb;
  1313.     short i;
  1314.     long r, narg;
  1315.     str bb = &bluff[1], bp, form = (kseeq ? xform : oform), tp, ttp;
  1316.     bool noret = false, narged;
  1317.     bool collor = color & colorful & y->infoed && !kseeq && !zortless;
  1318.     static long systagses[3] = { SYS_UserShell, TRUE, TAG_DONE };
  1319.     static str weekdaynames[7] = { "Sunday   ", "Monday   ", "Tuesday  ",
  1320.             "Wednesday", "Thursday", "Friday   ", "Saturday " };
  1321.     void mob(char c, str b);
  1322.  
  1323.     if (kseeq & ctifle)
  1324.     return;
  1325.     bluff[0] = ' ';        /* so bp[-1] always sees something */
  1326.     if (y->fflags & F_ROOT)
  1327.     prepath[0] = '\0';
  1328.     for (bp = bb; *form && bp - bb < 256; form++)
  1329.     if (*form == '\\') {
  1330.         narg = 0, narged = false;
  1331.         while (isdigit(c = *++form))
  1332.         narg = narg * 10 + c - '0', narged = true;
  1333.         switch (lower(c)) {
  1334.         case 'n':
  1335.             *(bp++) = '\n';
  1336.             break;
  1337.         case 'e':
  1338.             *(bp++) = 27;
  1339.             break;
  1340.         case '+':
  1341.             noret = true;
  1342.             break;
  1343.         case '/':
  1344.             cb = bp[-1];
  1345.             if (cb != '/' && cb != ':' && cb != '"' && cb > ' ')
  1346.             *(bp++) = '/';
  1347.             break;
  1348.         case '?':
  1349.             if ((cb = bp[-1]) != '/' && cb != ':')
  1350.             if (y->fflags & F_ROOT)
  1351.                 *(bp++) = ':';
  1352.             else if (y->dirred && !y->softlink)
  1353.                 *(bp++) = '/';
  1354.             break;
  1355.         case 'i':
  1356.             if (!narged)
  1357.             narg = 1;
  1358.             narg *= rdepth;
  1359.             if (bp - bb > 255 - narg)
  1360.             goto aarrgh;
  1361.             for (i = 0; i < narg; i++)
  1362.             *(bp++) = ' ';
  1363.             break;
  1364.         case 't':
  1365.             if (!narged)
  1366.             narg = 18;
  1367.             if (bp - bb >= 256 - narg)
  1368.             goto aarrgh;
  1369.             formdate(bp, &y->when, narg);
  1370.             bp += narg;
  1371.             break;
  1372.         case 'w':
  1373.             if (!narged)
  1374.             narg = 9;
  1375.             if (bp - bb >= 256 - narg)
  1376.             goto aarrgh;
  1377.             while (narg > 9)
  1378.             *bp++ = ' ', narg--;
  1379.             strncpy(bp, weekdaynames[y->when.ds_Days % 7], narg);
  1380.             bp += narg;
  1381.             *bp = 0;
  1382.             break;
  1383.         case 'b':
  1384.             if (bp - bb >= 248)
  1385.             goto aarrgh;
  1386.             fortection(bp, y->tection);
  1387.             bp += 8;
  1388.             break;
  1389.         case 's':
  1390.             i = (y->dirred ? 0 : digits(y->length));
  1391.             if (!narged)
  1392.             narg = 9;
  1393.             if (i > narg)
  1394.             narg = i;
  1395.             if (bp - bb >= 256 - narg)
  1396.             goto aarrgh;
  1397.             memset(bp, ' ', narg - i);
  1398.             if (!y->dirred)
  1399.             RawDoFmt("%ld", &y->length, mob, bp + narg - i);
  1400.             bp += narg;
  1401.             break;
  1402.         case 'k':
  1403.             i = digits(y->keee);
  1404.             if (!narged)
  1405.             narg = 6;
  1406.             if (i > narg)
  1407.             narg = i;
  1408.             if (bp - bb >= 256 - narg)
  1409.             goto aarrgh;
  1410.             memset(bp, ' ', narg - i);
  1411.             RawDoFmt("%ld", &y->keee, mob, bp + narg - i);
  1412.             bp += narg;
  1413.             break;
  1414.         case 'd':
  1415.             tp = prepath + strlen(prepath) - 1;
  1416.             if (notadirbutokay) {
  1417.             ttp = tp;
  1418.             if (!*prepath || (*tp == '/' &&
  1419.                         (tp[-1] == '/' || tp < prepath)))
  1420.                 tp++;
  1421.             else
  1422.                 while (--tp >= prepath)
  1423.                 if (*tp == '/' || *tp == ':')
  1424.                     goto flarp;
  1425.                 tp = ttp;
  1426.             }
  1427.           flarp:
  1428.             c = *++tp;
  1429.             *tp = 0;
  1430.             if (!(bp = Joyn(bp, bb, prepath))) {
  1431.             *tp = c;
  1432.             goto aarrgh;
  1433.             }
  1434.             *tp = c;
  1435.             break;
  1436.         case 'f':
  1437.             if (collor && !(bp = Joyn(bp, bb, CSI "33m")))
  1438.             goto aarrgh;
  1439.             if (!(bp = Joyn(bp, bb, y->name)))
  1440.             goto aarrgh;
  1441.             if (collor && !(bp = Joyn(bp, bb, CSI "31m")))
  1442.             goto aarrgh;
  1443.             break;
  1444.         case 'p':
  1445.             if (collor && !(bp = Joyn(bp, bb, CSI "33m")))
  1446.             goto aarrgh;
  1447.             if (!(bp = Joyn(bp, bb, prepath)))
  1448.             goto aarrgh;
  1449.             if (y->fflags & F_ROOT || !*prepath || (!notadirbutokay &&
  1450.                     (!csternal || zurse || consumption))) {
  1451.             if (*prepath && bp[-1] != '/' && bp[-1] != ':')
  1452.                 *(bp++) = '/';
  1453.             if (!(bp = Joyn(bp, bb, y->name)))
  1454.                 goto aarrgh;
  1455.             }
  1456.             if (collor && !(bp = Joyn(bp, bb, CSI "31m")))
  1457.             goto aarrgh;
  1458.             break;
  1459.         default:
  1460.             *(bp++) = *form;
  1461.         }
  1462.     } else
  1463.         *(bp++) = *form;
  1464.   aarrgh:
  1465.     *bp = 0;
  1466.     CCch();
  1467.     if (*form) {
  1468.     putfmt("\n *** Line produced by \"%s\" too long!\n", y->name);
  1469.     if (!hair) hair = ERROR_LINE_TOO_LONG;
  1470.     } else if (!abort) {
  1471.     if (kseeq) {
  1472.         pflush();
  1473.         r = System(bb, (adr) systagses);
  1474.         if (r >= 10) {
  1475.         putfmt(
  1476. "\n *** QUITTING; Command returned %ld, error code %ld.\n", r, me->pr_Result2);
  1477.         abort = 10;
  1478.         }
  1479.     } else {
  1480.         put(bb);
  1481.         if (!noret) {
  1482.         puch('\n');
  1483.         CCch();
  1484.         } else if (*bb)
  1485.         needsnl = true;
  1486.     }
  1487.     }
  1488. }
  1489.  
  1490.  
  1491.  
  1492. void Cough1(register flip y, bool dirs, short *col)
  1493. {
  1494.     register short lused = strlen(y->name);
  1495.     bool icon = color & colorful & y->infoed;
  1496.     char p, buf1[10], buf2[33];
  1497. #ifdef DEBUG
  1498.     if (!stricmp(y->name, "moned.readme"))
  1499.     CCch();
  1500. #endif
  1501.  
  1502.     if (ctifle) {
  1503.     abort = 1;    /* we found one, we found one! */
  1504.     return;
  1505.     }
  1506.     if (zortless) {
  1507.     song = 9;    /* extreme cases could overflow these */
  1508.     keygits = 6;
  1509. #ifdef MARKLINKS
  1510.     anylinks = true;
  1511. #endif
  1512.     anyfiles = true;
  1513.     }
  1514.     if (consumption && !cortless)
  1515.     return;
  1516.     if (anyfiles && zize) {
  1517.     if (y->dirred)
  1518.         pad(song);
  1519.     else
  1520.         padong(y->length, song);
  1521.     if (zomplete || !y->dirred || *col || steil)
  1522.         puch(' ');
  1523.     lused += song + 1;
  1524.     }
  1525.     if (!steil && !y->dirred && !*col && !zize && !zortless)
  1526.     puch(' ');            /* dent */
  1527.     if (ceys && (zomplete || !zortless)) {
  1528.     puch('[');
  1529.     padong(y->keee, keygits);
  1530.     put("] ");
  1531.     lused += 3 + keygits;
  1532.     }
  1533. /*  if (!zons) {
  1534.     puch(y->infoed ? FLACK : ' ');
  1535.     lused++;
  1536.     } */
  1537.     if (zomplete) {
  1538.     fortection(buf1, y->tection);
  1539.     formdate(buf2, &y->when, 18);
  1540. #ifdef MARKLINKS
  1541.     putfmt("%s %s ", buf1, buf2);
  1542.     if (anylinks) {
  1543.         if (y->fflags & F_HARDLINK)
  1544.         put("H> ");
  1545.         else if (y->softlink)
  1546.         put("S> ");
  1547.         else
  1548.         put("   ");
  1549.     } else
  1550.         puch(' ');
  1551. #else
  1552.     putfmt("%s %s  ", buf1, buf2);
  1553. #endif
  1554.     }
  1555.     if (icon)
  1556.     put(CSI "33m");
  1557.     if (zortless) {
  1558.     put(prepath);
  1559.     if (*prepath) {
  1560.         register short l = strlen(prepath);
  1561.         p = prepath[l - 1];
  1562.         if (p != '/' && p != ':')
  1563.         puch('/');
  1564.     }
  1565.     }
  1566.     put(y->name);
  1567.     if (icon)
  1568.     put(CSI "31m");
  1569.     if (y->dirred && !y->softlink) {
  1570.     puch(y->fflags & F_ROOT ? ':' : '/');
  1571.     lused++;
  1572.     }
  1573.     if (zomplete) {
  1574.     puch('\n');
  1575.     if (y->softlink > 0 && y->slinky)
  1576.         putfmt(" >>> soft link to \"%s\"\n", y->slinky);
  1577.     else if (y->softlink < 0) {
  1578.         pflush();
  1579.         PrintFault(-(long) y->slinky, " *** Couldn't read soft link");
  1580.     }
  1581.     if (y->comment)
  1582.         putfmt(": %s\n", y->comment);
  1583.     } else if (zortless)
  1584.     puch('\n');
  1585.     else {
  1586.     if (++*col >= cols) {
  1587.         *col = 0;
  1588.         puch('\n');
  1589.     } else
  1590.         pad(cwid - lused);
  1591.     }
  1592. }
  1593.  
  1594.  
  1595.  
  1596. bool CoughHalf(flip flist, bool dirs)
  1597. {
  1598.     short h, col = 0, n = 0;
  1599.     bool anyleft = false;
  1600.     register flip y;
  1601.  
  1602.     if (!flist || zortless)
  1603.     return false;
  1604.     for (y = flist; y; y = y->next)
  1605.     if (!y->jected)
  1606.         if (steil == 3 || !(dirs ^ y->dirred)) {
  1607.         y->wanted = true;
  1608.         n++;
  1609.         } else {
  1610.         y->wanted = false;
  1611.         anyleft = true;
  1612.         }
  1613.     /* else wanted is always false */
  1614.     if (!n)
  1615.     return false;
  1616.     h = 0;
  1617.     for (y = flist; y; y = y->next)
  1618.     if (y->wanted) {
  1619.         y->ordination = h;
  1620.         if (colsort) {
  1621.         if ((h += cols) >= n)
  1622.             h = h % cols + 1;
  1623.         } else h++;
  1624.     } else
  1625.         y->ordination = -1;
  1626.     y = flist;
  1627.     for (h = 0; h < n; h++) {
  1628.     while (y->ordination != h)
  1629.         if (!(y = y->next))
  1630.         y = flist;        /* rather inefficient... */
  1631.     CCch();
  1632.     if (abort) break;
  1633.     if (oform)
  1634.         FloorMat(y, false);
  1635.     else
  1636.         Cough1(y, dirs, &col);
  1637.     }
  1638.     if (col > 0)
  1639.     puch('\n');
  1640.     return anyleft && !abort && !zutbot;
  1641. }
  1642.  
  1643.  
  1644.  
  1645. /* short */ void plural(str s, long n1, long n2 /* , short diffs */ )
  1646. {
  1647.     str ss = (n1 == 1 && !n2 ? "" : "s");
  1648.     if (!n2)
  1649.     putfmt("%ld ", n1);
  1650.     else {
  1651. /**    diffs++;                    **/
  1652.     n2 += n1;
  1653. /**     if (diffs > 1)                    **/
  1654.         putfmt("%ld (%ld) ", n1, n2);
  1655. /**    else putfmt("%ld (of %ld) ", n1, n2);        **/
  1656.     }
  1657.     putfmt(s, ss);
  1658. /** return diffs;                    **/
  1659. }
  1660.  
  1661.  
  1662.  
  1663. void Tote(register struct cuont *c)
  1664. {
  1665. /** short diffs = 0;                    **/
  1666.     if (!c[1].dir && !c[1].fil && c[0].dir + c[0].fil <= 1) {
  1667.     if (!c[0].dir && !c[0].fil) return;        /**** I DUNNO... */
  1668.     putfmt("1 %s, ", c[0].dir ? "dir" : "file");
  1669.     plural("block%s.\n", c[0].blok, 0L /* , 0 */ );
  1670.     } else {
  1671.     /* diffs = */ plural("dir%s, ", c[0].dir, c[1].dir /* , diffs */ );
  1672.     /* diffs = */ plural("file%s, ", c[0].fil, c[1].fil /* , diffs */ );
  1673.     /* diffs = */ plural("byte%s, ", c[0].byt, c[1].byt /* , diffs */ );
  1674.     plural("block%s.\n", c[0].blok, c[1].blok /* , diffs */ );
  1675.     }
  1676. }
  1677.  
  1678.  
  1679.  
  1680. void Header(short ficou, str name)
  1681. {
  1682.     str nn = *name ? name : "(current dir)";
  1683.     CCch();
  1684.     if (abort)
  1685.     return;
  1686.     if (didaninny && (!consumption || oform || cortless))
  1687.     puch('\n');
  1688.     if (ficou)
  1689.     putfmt("        ===  %s  ===\n", nn);
  1690.     else
  1691.     putfmt("        ===  %s is empty.  ===\n", nn);
  1692.     didaninny = true;
  1693. }
  1694.  
  1695.  
  1696.  
  1697. void CoughUp(flip flist, short ficou)
  1698. {
  1699.     cols = 1;        /* default */
  1700. #ifdef MARKLINKS
  1701.     anylinks = false;
  1702. #endif
  1703.     anyfiles = false;
  1704.     Columnate(flist, ficou);
  1705.     if (cupside) {
  1706.     zuttop = zutfils;
  1707.     zutbot = zutdirs;
  1708.     } else {
  1709.     zuttop = zutdirs;
  1710.     zutbot = zutfils;
  1711.     }
  1712.     if (!zuttop && CoughHalf(flist, !cupside) && !consumption)
  1713.     if (steil == 2) {
  1714.         short i;
  1715.         put("    ");
  1716.         for (i = 0; i < wid - 10; i++)
  1717.         puch('-');
  1718.         puch('\n');
  1719.     } else if (steil == 1)
  1720.         puch('\n');
  1721.     CCch();
  1722.     if (steil != 3 && !zutbot && !abort)
  1723.     CoughHalf(flist, cupside);
  1724. }
  1725.  
  1726.  
  1727.  
  1728. void Tad(register struct cuont *des, register struct cuont *src)
  1729. {
  1730.     register short i;
  1731.     for (i = 0; i < 2; i++) {
  1732.     des->blok += src->blok;
  1733.     des->byt += src->byt;
  1734.     des->fil += src->fil;
  1735.     des->dir += src->dir;
  1736.     des++;
  1737.     src++;
  1738.     }
  1739. }
  1740.  
  1741.  
  1742.  
  1743. void DoInner(str what, BPTR deer, bool hasparent,
  1744.             struct Ants *ants, struct cuont *great)
  1745. {
  1746.     flip filist, f;
  1747.     bool bigger, heads, viz = false;
  1748.     struct cuont tot[2], gran[2];    /* [0] is non-jected, [1] is jected */
  1749.     struct Ants parant, *pa;
  1750.     short ficou, prength = strlen(prepath), preng1 = prength;
  1751.     char pe = prepath[prength - 1];
  1752.  
  1753.     gran[0].blok = gran[0].byt = gran[0].fil = gran[0].dir = 0;
  1754.     tot[1] = tot[0] = gran[1] = gran[0];
  1755.     parant.prav = ants;
  1756.     parant.kay = bip(struct FileLock, deer)->fl_Key;
  1757.     if (!abort && StackLeft() < STACKNEEDED) {
  1758.     putfmt("*** Cannot list \"%s\" -- insufficient stack space!\n", what);
  1759.     hair = ERROR_TOO_MANY_LEVELS;
  1760.     return;
  1761.     }
  1762.     if (prength && pe != ':' && pe != '/') {
  1763.     prepath[prength++] = '/';
  1764.     prepath[prength] = 0;
  1765.     }
  1766.     zurse = curse | canydepth;
  1767.     zortless = ctifle | cortless;
  1768.     zize = (complete || (cize && !zortless)) && !oform;
  1769.     zomplete = complete && !oform;
  1770.     zutfils = cutfils;
  1771.     zutdirs = cutdirs;
  1772.     if (hasparent || !complete || oform || !zortless) {
  1773.     if (prength < PREPENGTH - strlen(what)) {
  1774.         strcpy(prepath + prength, what);
  1775.         if (!hasparent && csternal)
  1776.         if (prepath[strlen(prepath) - 1] == ':')
  1777.             prepath[0] = 0;
  1778.         else
  1779.             *PathPart(prepath) = 0;
  1780.     }
  1781.     }
  1782.     for (pa = ants; pa; pa = pa->prav)
  1783.     if (pa->kay == parant.kay) {
  1784.         putfmt(" *** %s is its own ancestor, through a link!\n\n", prepath);
  1785.         /* don't set hair */
  1786.         goto skipit;
  1787.     }
  1788.     heads = (didaninny | morethan1) || hasparent || (cupside & zurse);
  1789.     if (heads && zortless && (zize | consumption) && !hasparent && !ctifle)
  1790.     Header(1, what);
  1791.     filist = ScanDeer(deer, &ficou, hasparent, ¶nt, gran);
  1792.     if (!filist && !abort && (me->pr_Result2 == ERROR_NO_FREE_STORE)) {
  1793.     putfmt("Not enough memory to list \"%s\"!\n", what);
  1794.     hair = ERROR_NO_FREE_STORE;
  1795.     }
  1796.     zize |= zomplete;
  1797.     if (!(consumption && !oform) && !cortless && !ctifle)
  1798.     filist = Soart(filist, ficou);
  1799.     for (f = filist; f; f = f->next) {
  1800.     register struct cuont *tt = tot + f->jected;
  1801.     if (f->dirred)
  1802.         tt->dir++;
  1803.     else {
  1804.         tt->byt += f->length;
  1805.         tt->fil++;
  1806.     }
  1807.     tt->blok += f->blox + 1;
  1808.     if (!f->jected && !(f->dirred ? zutdirs : zutfils))
  1809.         viz = true;
  1810.     }
  1811.     heads |= didaninny;
  1812.     if (heads && viz && !zortless)
  1813.     Header(ficou, prepath);
  1814.     if (!abort)
  1815.     CoughUp(filist, ficou);
  1816.     if ((consumption | zize) && !abort && !cortless && !ctifle) {
  1817.     if (notadir) {
  1818.         putfmt("\"%s\" is a file, not a directory.\n", prepath);
  1819.         hair = ERROR_OBJECT_WRONG_TYPE;
  1820.     } else
  1821.         Tote(tot);
  1822.     }
  1823.     CCch();
  1824.     if (cupside)
  1825.     for (f = filist; f; f = f->next)
  1826.         if (f->fflags & F_DESCEND)
  1827.         Descendify(f, deer, ¶nt, gran);
  1828.     Tad(gran, tot);
  1829.     if (great)
  1830.     Tad(great, gran);
  1831.     bigger = gran[0].blok + gran[1].blok > tot[0].blok + tot[1].blok;
  1832.     if (!abort && !ctifle && (consumption | zize)
  1833.                 && (cortless ? !hasparent : bigger)) {
  1834.     if (notadir) {
  1835.         putfmt("\"%s\" is a file, not a directory.\n", prepath);
  1836.         hair = ERROR_OBJECT_WRONG_TYPE;
  1837.     } else {
  1838.         if (bigger && !cortless)
  1839.         put("Total:  ");
  1840.         Tote(gran);
  1841.     }
  1842.     }
  1843.     while (filist) {
  1844.     f = filist->next;
  1845.     Lose(filist);
  1846.     filist = f;
  1847.     }
  1848.   skipit:
  1849.     prepath[preng1] = 0;        /* remove tail just added */
  1850. }
  1851.  
  1852.  
  1853.  
  1854. /* SplitTailPat takes dir/pat string in from and translates it into a compiled
  1855.    pattern in mesh (and uncompiled in pat) and a lock on the directory in deer
  1856.    to be scanned using the pattern, or if deer is also a pattern, a struct
  1857.    AnchorPath in ankh. */
  1858.  
  1859. bool SplitTailPat(str from, BPTR *deer)
  1860. {
  1861.     ubyte pat[PATLIMIT + 1];
  1862.     short w;
  1863.     register ustr p = FilePart(from);
  1864.  
  1865.     if (strlen(p) > PATLIMIT)
  1866.     w = -1;
  1867.     else {
  1868.     strcpy(pat, p);
  1869.     inatl_strupr(pat);
  1870.     w = ParsePatternNoCase(pat, mesh, PATLIMIT * 2 + 2L);
  1871.     }
  1872.     if (w < 0) {
  1873.     if (!ctifle)
  1874.         putfmt("Bogus pattern \"%s\".\n", p);
  1875.     hair = ERROR_LINE_TOO_LONG;
  1876.     return false;
  1877.     }
  1878.     if (w) {
  1879.     *(PathPart(from)) = 0;
  1880.     patty = true;
  1881.     }
  1882.     if (!deer)
  1883.     return true;
  1884.     if (w)
  1885.     *deer = RLock(from);
  1886.     if (!*deer) {
  1887.     if (ankh = AllocPZ(sizeof(*ankh) + ABSIZE - 1)) {
  1888.         strncpy(abspath, from, ABSIZE);
  1889.         abspath[ABSIZE] = 0;
  1890.         ankh->ap_Strlen = ABSIZE;
  1891.         if (hair = MatchFirst(abspath, ankh)) {
  1892.         FreeMem(ankh, sizeof(*ankh) + ABSIZE - 1);
  1893.         ankh = null;
  1894.         }
  1895.     } else
  1896.         hair = ERROR_NO_FREE_STORE;
  1897.     me->pr_Result2 = hair;
  1898.     if (hair && !ctifle && hair != ERROR_NO_MORE_ENTRIES) {
  1899.         putfmt("Couldn't list \"%s\"", from);
  1900.         if (hair == ERROR_BAD_TEMPLATE)
  1901.         putn(": bogus pattern.");
  1902.         else
  1903.         PFault("");
  1904.     }
  1905.     return !hair;
  1906.     }
  1907.     return true;
  1908. }
  1909.  
  1910.  
  1911.  
  1912. bool HandleMultiAssign(str what, str bowel)
  1913. {
  1914.     struct AssignList *al;
  1915.     struct DosList *dl;
  1916.     short l, locount = 1;
  1917.     BPTR lox[MULTILIMIT];
  1918.  
  1919.     if (dl = LockDosList(LDF_ALL | LDF_READ)) {
  1920.     *bowel = 0;
  1921.     dl = FindDosEntry(dl, what, LDF_ALL | LDF_READ);
  1922.     *bowel = ':';
  1923.     if (dl && dl->dol_Type == DLT_DIRECTORY
  1924.             && (al = dl->dol_misc.dol_assign.dol_List)) {
  1925.         if (patty && !SplitTailPat(bowel + 1, 0))
  1926.         return false;
  1927.         lox[0] = DupLock(dl->dol_Lock);
  1928.         while (al && locount < MULTILIMIT) {
  1929.         lox[locount++] = DupLock(al->al_Lock);
  1930.         al = al->al_Next;
  1931.         }
  1932.         UnLockDosList(LDF_ALL | LDF_READ);
  1933.         morethan1 = true;
  1934.         for (l = 0; l < locount; l++)
  1935.         if (lox[l]) {
  1936.             if (!abort)
  1937.             if (NameFromLock(lox[l], abspath, ABSIZE))
  1938.                 DoInner(abspath, lox[l], null, null, null);
  1939.             else {
  1940.                 didaninny = true;
  1941.                 pflush();
  1942.                 PrintFault(me->pr_Result2,
  1943.                      "\n *** skipping one assignment");
  1944.             }
  1945.             UnLock(lox[l]);
  1946.         } /* else... ?  lox[l] should never be null */
  1947.         return true;
  1948.     }
  1949.     UnLockDosList(LDF_ALL | LDF_READ);
  1950.     }
  1951.     return false;
  1952. }
  1953.  
  1954.  
  1955.  
  1956. void Do(str what)
  1957. {
  1958.     BPTR deer = -1, ocd;
  1959.     short wl = strlen(what);
  1960.     register str p = what + wl, pp;
  1961.  
  1962.     notadir = notadirbutokay = false;
  1963.     if (wl >= 2 && (canydepth = p[-1] == ':' && p[-2] == ':'))
  1964.     p[-2] = 0;
  1965.     if (csternal && !consumption) {
  1966.     char beastspace[260];
  1967.     ubyte buf[SOFTSIZE];
  1968.     struct DevProc *dp = null;
  1969.     str beast = (str) (((ulong) &beastspace[3]) & ~3);
  1970.  
  1971.     do {
  1972.         dp = GetDeviceProc(what, dp);
  1973.         if (dp) {
  1974.         beast[0] = wl;
  1975.         strncpy(beast + 1, what, (size_t) wl);
  1976.         if (deer = DoPkt3(dp->dvp_Port, (long) ACTION_LOCATE_OBJECT,
  1977.                     dp->dvp_Lock, (long) beast >> 2,
  1978.                     (long) ACCESS_READ))
  1979.             break;
  1980.         if (me->pr_Result2 == ERROR_IS_SOFT_LINK)
  1981.             if (ctifle) {
  1982.             abort = 1;
  1983.             return;
  1984.             } else {
  1985.             long suck = ReadLink(dp->dvp_Port, dp->dvp_Lock,
  1986.                         what, buf, SOFTSIZE);
  1987.             if (suck)
  1988.                 putfmt("%s is a soft link to \"%s\".\n", what, buf);
  1989.             else {
  1990.                 hair = me->pr_Result2;
  1991.                 putfmt("*** soft link %s ", what);
  1992.                 PFault("UNREADABLE");
  1993.             }
  1994.             FreeDeviceProc(dp);
  1995.             return;
  1996.             }
  1997.         }
  1998.     } while (dp);
  1999.     FreeDeviceProc(dp);
  2000.     }
  2001.     p = strchr(what, ':');
  2002.     if (p && (pp = p + 1, !*pp || (!strchr(pp, '/')
  2003.             && (deer == -1 ? !(deer = RLock(what)) : !deer)))) {
  2004.     patty = !!*pp;
  2005.     if (HandleMultiAssign(what, p)) {
  2006.         if (deer && deer != -1)
  2007.         UnLock(deer);
  2008.         return;
  2009.     }
  2010.     }
  2011.     if (deer == -1)
  2012.     deer = RLock(what);
  2013.     if (!deer)
  2014.     if (!SplitTailPat(what, &deer))
  2015.         return;
  2016.     if (ankh) {
  2017.     morethan1 = true;
  2018.     do {
  2019.         if (!patty || ankh->ap_Info.fib_EntryType > 0) {
  2020.         ocd = CurrentDir(ankh->ap_Current->an_Lock);
  2021.         if (deer = RLock(ankh->ap_Info.fib_FileName)) {
  2022.             *prepath = 0;
  2023.             if (!convertpath ||    !NameFromLock(deer, abspath, ABSIZE))
  2024.             strcpy(abspath, ankh->ap_Buf);
  2025.             DoInner(abspath, deer, null, null, null);
  2026.             UnLock(deer);
  2027.         } else {
  2028.             hair = me->pr_Result2;
  2029.             putfmt(" *** Can't lock directory \"%s\"!\n", abspath);
  2030.         }
  2031.         CurrentDir(ocd);
  2032.         }
  2033.         CCch();
  2034.     } while (!abort && !MatchNext(ankh));
  2035.     MatchEnd(ankh);
  2036.     FreeMem(ankh, sizeof(*ankh) + ABSIZE - 1);
  2037.     ankh = null;
  2038.     } else {
  2039.     if (!deer) {
  2040.         if (!ctifle)
  2041.         putfmt("Couldn't find \"%s\".\n", what);
  2042.         hair = ERROR_OBJECT_NOT_FOUND;
  2043.         return;
  2044.     }
  2045.     if (convertpath && NameFromLock(deer, abspath, ABSIZE))
  2046.         what = abspath;
  2047.     *prepath = 0;
  2048.     DoInner(what, deer, null, null, null);
  2049.     UnLock(deer);
  2050.     }
  2051. }
  2052.  
  2053.  
  2054.  
  2055. void Opt(register ustr p)
  2056. {
  2057.     register ubyte c;
  2058.     for (c = lower(*++p) ; c > ' '; c = lower(*++p)) {
  2059.     switch (c) {
  2060.         case 'r':  curse ^= true; continue;
  2061.         case 'i':  cons ^= true; continue;
  2062.         case 'm':  colorful ^= true; continue;
  2063.         case 's':  cize ^= true; continue;
  2064.         case 'c':  cron ^= true; continue;
  2065.         case 'l':  complete ^= true; continue;
  2066.         case 'h':  colsort ^= true; continue;
  2067.         case 'f':  cutdirs ^= true; cutfils = false; continue;
  2068.         case 'd':  cutfils ^= true; cutdirs = false; continue;
  2069.         case 'x':  csternal ^= true; continue;
  2070.         case 'o':  cortless ^= true; continue;
  2071.         case 'u':  consumption ^= true; continue;
  2072.         case 'k':  ceys ^= true; continue;
  2073.      /* case 'g':  cage ^= true; continue;      */
  2074.         case 'v':  creverse ^= true; continue;
  2075.         case 'y':  cweeek ^= true; continue;
  2076.         case 'z':  cizesort ^= true; continue;
  2077.         case '!':  cursorhide = true; continue;    /* for EnvOpts */
  2078.         case '?':  continue;            /* likewise */
  2079.         case '-':  continue;            /* likewise */
  2080.         case '`':  continue;            /* lower('@') == '`' */
  2081.         case 't':  convertpath ^= true; continue;
  2082.         case 'p':  {
  2083.         register bool tilt;
  2084.         register short b;
  2085.         protlook = protwant = 0;
  2086.         while ((c = lower(p[1])) > ' ') {
  2087.             if (tilt = (c == '~')) {        /* lower('^') == '~' */
  2088.             c = lower((++p)[1]);
  2089.             if (c <= ' ')
  2090.                 break;
  2091.             }
  2092.             p++;
  2093.             switch (c) {
  2094.             case 'h':  b = 7; break;
  2095.             case 's':  b = 6; break;
  2096.             case 'p':  b = 5; break;
  2097.             case 'a':  b = 4; break;
  2098.             case 'r':  b = 3; break;
  2099.             case 'w':  b = 2; break;
  2100.             case 'e':  b = 1; break;
  2101.             case 'd':  b = 0; break;
  2102.             default:  b = -1;
  2103.             }
  2104.             if (b < 0)
  2105.             putn(
  2106. " *** letters after -P must be any of H S P A R W E D.");
  2107.             else {
  2108.             protlook |= bit(b);
  2109.             if (tilt ^ (b >= 4))
  2110.                 protwant |= bit(b);
  2111.             }
  2112.         }
  2113.         continue;
  2114.         }
  2115.         case 'a':  case 'b':  case 'n':  {
  2116.         register short age = -1;
  2117.         register char cc;
  2118.         while ((cc = p[1]) >= '0' && cc <= '9') {
  2119.             if (age < 0) age = cc - '0';
  2120.             else age = 10 * age + cc - '0';
  2121.             p++;
  2122.         }
  2123.         if (c == 'a')
  2124.             after = age + 1;
  2125.         else if (c == 'b')
  2126.             before = age + 1;
  2127.         else {
  2128.             if (cupside = (age >= 10))
  2129.             age -= 10;
  2130.             if (age >= 0 && age <= TOPSTYLE)
  2131.             steil = age;
  2132.             else
  2133.             putfmt(" *** -N must be followed by a number from "
  2134.                     "0 to %d or 10 to %d.\n",
  2135.                     TOPSTYLE, TOPSTYLE + 10);
  2136.         }
  2137.         continue;
  2138.         }
  2139.         case '{':                /* lower('[') == '{' */
  2140.         if (*(p++) == '[') {
  2141.             if (*p) oform = (str) p;
  2142.             else oform = null;
  2143.         } else {
  2144.             if (*p) xform = (str) p;
  2145.             else xform = null;
  2146.         }
  2147.         return;
  2148.     }
  2149.     putfmt(" *** Unknown option -%c.\n", toupper(*p));
  2150.     }
  2151. }
  2152.  
  2153.  
  2154.  
  2155. void DoEnvOpts(void)
  2156. {
  2157.     ubyte brack;
  2158.     ustr p, q, hole, op = abspath;
  2159.     short l;
  2160.     APTR owp = me->pr_WindowPtr;
  2161.  
  2162.     me->pr_WindowPtr = (APTR) -1;
  2163.     l = GetVar("DR-OPTS", op, ABSIZE, 0L);
  2164.     me->pr_WindowPtr = owp;
  2165.     if (l <= 0)
  2166.     return;
  2167.     do {
  2168.     hole = null;
  2169.     for (p = op; *p > ' '; p++)
  2170.         if (*p == '[' || *p == '{') {
  2171.         brack = *p + 2;            /* [ -> ], { -> } */
  2172.         for (q = p + 1; *q && *q != brack; q++)
  2173.             if (*q == '\\' && q[1])
  2174.             q++;
  2175.         if (*q) {
  2176.             *(hole = q) = 0;
  2177.             break;
  2178.         }
  2179.         }
  2180.     Opt(op - 1);
  2181.     if (hole)
  2182.         op = hole + 1;
  2183.     else
  2184.         while (*op > ' ') op++;
  2185.     while (*op && *op <= ' ') op++;
  2186.     } while (*op);
  2187. }
  2188.  
  2189.  
  2190.  
  2191. void mane(void)
  2192. {
  2193.     struct FileHandle *out = gbip(me->pr_COS);
  2194.     short a, aa;
  2195.     bool help = argc == 2 && (ubyte) *argv[1] == HELP && !argv[1][1];
  2196.  
  2197.     if (help) {        /* -cdfhiklorsux | -a# | -b# | -p... | -{...} | -[...] */
  2198.     putfmt("\nDr " VERSION " by Paul Kienitz"
  2199.         " -- Usage: %s {directories|patterns|-options} ...\n\n"
  2200.         "where options are:\n%s", *argv, helpslab);
  2201.     abort = 5;
  2202.     return;
  2203.     }
  2204.     if (!cancelenvar)
  2205.     DoEnvOpts();
  2206. #ifdef DEBUG
  2207.  if (cancelenvar) {
  2208.   Write(Output(), "Press return: ", 14);
  2209.   Read(Input(), &a, 1);
  2210.  }
  2211. #endif
  2212.     color =  !!IsInteractive(me->pr_COS);        /* -1 => 1 */
  2213.     wid = CheckWindowWidth();                /* may reset color */
  2214.     if (color & cursorhide)
  2215.     Write(me->pr_COS, CSI "0 p", 4);        /* cursor off */
  2216.     for (aa = argc - 1; aa > 0 && (ubyte) *argv[aa] == HYPH; aa--)
  2217.     Opt(argv[aa]);
  2218.     morethan1 = argc - hyphc > 2;
  2219.     if (aa) {
  2220.     for (a = 1; a <= aa && !abort; a++)
  2221.         if ((ubyte) *argv[a] == HYPH)
  2222.         Opt(argv[a]);
  2223.         else {
  2224.         if ((ubyte) *argv[a] == HELP) *argv[a] = '?';
  2225.         Do(argv[a]);
  2226.         }
  2227.     } else
  2228.     Do("");
  2229.     if (needsnl)
  2230.     puch('\n');
  2231.     if (hair && !ctifle && hair != ERROR_OBJECT_NOT_FOUND
  2232.             && hair != ERROR_OBJECT_WRONG_TYPE && !abort) {
  2233.     PFault("\n*** LISTING NOT COMPLETE");
  2234.     }
  2235.     pflush();
  2236.     if (color & cursorhide)
  2237.     Write(me->pr_COS, CSI "1 p", 4);    /* cursor on */
  2238. }
  2239.  
  2240.  
  2241.  
  2242. /* This version of cliparse NO LONGER uses *" to mean a quote inside quotes;
  2243.    instead it uses "", because we need * for wildcards now.  It marks -args with
  2244.    a special character (HYPH), accepting a -arg in quotes as a filename.  *N and
  2245.    *E are not supported, since filenames can't contain newline or escape.  It
  2246.    parses strings inside -[ ] or -{ } without stopping for spaces.  It converts
  2247.    any unquoted question mark at the start of a word into another special
  2248.    character (HELP).
  2249.  */
  2250.  
  2251. void cliparse(long alen, str aptr)
  2252. {
  2253.     register short coml;
  2254.     bool quoted, litting, lit, hyped = false;
  2255.     register char c;
  2256.     char litter, unquote;
  2257.     str tempargv[200];
  2258.     register str ap = aptr;
  2259.     register str poik = bip(char, bip(struct CommandLineInterface,
  2260.                       me->pr_CLI)->cli_CommandName);
  2261.  
  2262.     hyphc = 0;
  2263.     coml = *poik;
  2264.     arglen = coml + (short) alen + ((short) alen >> 2) + 1;
  2265.     if (!(argline = Alloc(arglen))) return;
  2266.     strncpy(argline, poik + 1, (size_t) coml);
  2267.     argline[coml] = '\0';
  2268.     *tempargv = argline;
  2269.     argc = 1;
  2270.     aptr = ap + alen;        /* now marks the end instead of the start */
  2271.     if (alen > 0) {
  2272.     tempargv[1] = poik = argline + coml + 1;
  2273.     for (;;) {
  2274.         if ((ubyte) *ap <= ' ') {
  2275.         hyped = false;
  2276.         while (ap < aptr && (ubyte) *ap <= ' ') ap++;
  2277.         }
  2278.         if (ap >= aptr) break;
  2279.         if (*ap == '-') {
  2280.         hyphc++;
  2281.         *(poik++) = HYPH;
  2282.         ap++;
  2283.         hyped = true;
  2284.         }
  2285.         quoted = lit = false;
  2286.         if (hyped && lower(*ap) == '{') {
  2287.         quoted = true;
  2288.         litter = '\\';
  2289.         if (*ap == '[') unquote = ']';
  2290.         else unquote = '}';
  2291.         } else if (!hyped && *ap == '"') {
  2292.         ap++;
  2293.         quoted = true;
  2294.         litter = unquote = '"';
  2295.         } else if (!hyped && *ap == '?')
  2296.         *ap = HELP;
  2297.         while (c = *ap, litting = quoted && !lit && c == litter
  2298.                     && (litter != '"' || ap[1] == '"'),
  2299.             (ap < aptr && (quoted ? lit || litting || c != unquote
  2300.                           : (ubyte) c > ' ')
  2301.              && (!hyped || quoted || lower(c) != '{'))) {
  2302.         lit = litting;
  2303.         if (!lit || litter == '\\')
  2304.             *(poik++) = c;
  2305.         if (hyped && !quoted) {
  2306.             if (c == '!')
  2307.             cursorhide = true;
  2308.             else if (c == '@')
  2309.             cancelenvar = true;
  2310.             else if (c == '?')
  2311.             ctifle = true;
  2312.         }
  2313.         ap++;
  2314.         }
  2315.         if (quoted && *ap == unquote) ap++;
  2316.         *(poik++) = '\0';
  2317.         tempargv[++argc] = poik;
  2318.         if ((quoted && litter == '\\' && (ubyte) *ap > ' ')
  2319.                 || (hyped && lower(c) == '{')) {
  2320.         *(poik++) = HYPH;    /* kluge for args around [] or {} */
  2321.         hyphc++;
  2322.         }
  2323.     }
  2324.     }
  2325.     tempargv[argc] = null;
  2326.     if (!(argv = Alloc((argc + 1) << 2))) {
  2327.     argc = 0;
  2328.     FreeMem(argline, (long) arglen);
  2329.     }
  2330.     for (coml = 0; coml <= argc; coml++)
  2331.     argv[coml] = tempargv[coml];
  2332. }
  2333.  
  2334.  
  2335.  
  2336. /* simplified reentrant startup code */
  2337.  
  2338. long _main(long alen, str aptr)
  2339. {
  2340.     adr reta;
  2341.  
  2342.     me = ThisProcess();
  2343.     reta = me->pr_ReturnAddr;
  2344.     if (!me->pr_CLI) {
  2345.     /**** maybe reply wbstartup msg here?  naah */
  2346.     return 999999L;        /* never gets unloaded by workbench */
  2347.     }
  2348.     stacklimit = (adr) ((str) reta - *(long *) reta + 4);
  2349.     if (((struct Library *) DOSBase)->lib_Version < 37) {
  2350.     Write(me->pr_COS, (ustr)
  2351.         "This version of Dr requires AmigaDOS 2.04 or newer.\n", 52L);
  2352.     me->pr_Result2 = ERROR_INVALID_RESIDENT_LIBRARY;
  2353.     return 20L;
  2354.     }
  2355.     if (OpenSmallIO(CCch)) {
  2356.     me->pr_Result2 = ERROR_NO_FREE_STORE;
  2357.     return 20L;
  2358.     }
  2359.     colsort = colorful = true;
  2360.     cliparse(alen, aptr);
  2361.     if (argc)
  2362.     mane();
  2363.     else {
  2364.     putfmt("Dr:  Not enough memory to start up!\n");
  2365.     hair = ERROR_NO_FREE_STORE;
  2366.     abort = 20;
  2367.     }
  2368.     if (argline) {
  2369.     FreeMem(argline, (long) arglen);
  2370.     FreeMem(argv, (long) (argc + 1) << 2);
  2371.     }
  2372.     CloseSmallIO();
  2373.     if (hair) {
  2374.     me->pr_Result2 = hair;
  2375.     if (!abort) abort = (hair == ERROR_LINE_TOO_LONG ? 5 : 10);
  2376.     }
  2377.     if (ctifle)
  2378.     if (abort == 1)
  2379.         abort = 0, me->pr_Result2 = 0;
  2380.     else if (!abort)
  2381.         abort = 5, me->pr_Result2 = ERROR_OBJECT_NOT_FOUND;
  2382.     return (long) abort;
  2383. }
  2384.